// -*- C++ -*-
/***************************************************************************
 *
 * istream - Declarations for the Standard Library istreams
 *
 * $Id: //stdlib/dev/include/istream#53 $
 *
 ***************************************************************************
 *
 * Copyright (c) 1994-2005 Quovadx,  Inc., acting through its  Rogue Wave
 * Software division. Licensed under the Apache License, Version 2.0 (the
 * "License");  you may  not use this file except  in compliance with the
 * License.    You    may   obtain   a   copy   of    the   License    at
 * http://www.apache.org/licenses/LICENSE-2.0.    Unless   required    by
 * applicable law  or agreed to  in writing,  software  distributed under
 * the License is distributed on an "AS IS" BASIS,  WITHOUT WARRANTIES OR
 * CONDITIONS OF  ANY KIND, either  express or implied.  See  the License
 * for the specific language governing permissions  and limitations under
 * the License.
 * 
 **************************************************************************/

#ifndef _RWSTD_ISTREAM_INCLUDED
#define _RWSTD_ISTREAM_INCLUDED

#if __GNUG__ >= 3
#  pragma GCC system_header
#endif   // gcc >= 3

#ifndef _RWSTD_NO_REDUNDANT_DEFINITIONS
#  include <ostream>
#endif   // _RWSTD_NO_REDUNDANT_DEFINITIONS

#include <loc/_num_get.h>
#include <loc/_num_put.h>

#include <rw/_basic_ios.h>
#include <rw/_mutex.h>
#include <rw/_traits.h>

#include <rw/_defs.h>


_RWSTD_NAMESPACE (__rw) {

_EXPORT
template <class _CharT, class _Traits, class _NativeType>
_STD::basic_istream<_CharT, _Traits>&
__rw_extract (_STD::basic_istream<_CharT, _Traits>&, _NativeType&);

}   // namespace __rw


_RWSTD_NAMESPACE (std) {

#ifndef _RWSTD_IOSFWD_INCLUDED

_EXPORT
template <class _CharT,
          class _Traits _RWSTD_COMPLEX_DEFAULT (char_traits<_CharT>) >
class basic_istream;

_EXPORT
template <class _CharT,
          class _Traits _RWSTD_COMPLEX_DEFAULT (char_traits<_CharT>) >
class basic_iostream;

typedef basic_istream<char, char_traits<char> >  istream;
typedef basic_iostream<char, char_traits<char> > iostream;

#  ifndef _RWSTD_NO_WCHAR_T

typedef basic_istream<wchar_t, char_traits<wchar_t> >  wistream;
typedef basic_iostream<wchar_t, char_traits<wchar_t> > wiostream;

#  endif   // _RWSTD_NO_WCHAR_T
#endif   // _RWSTD_IOSFWD_INCLUDED


_EXPORT
template <class _CharT, class _Traits>
class basic_istream: virtual public basic_ios<_CharT, _Traits>
{
public:

    typedef _CharT                                    char_type;
    typedef _Traits                                   traits_type;
    typedef _TYPENAME traits_type::int_type           int_type;
    typedef _TYPENAME traits_type::pos_type           pos_type;
    typedef _TYPENAME traits_type::off_type           off_type;
      
    // 27.6.1.1.1, p1
    _EXPLICIT basic_istream (basic_streambuf<char_type, traits_type> *__sb)
        : _C_gcount (0) {
        this->init (__sb);
    }

    // called from sentry's ctor to prepare stream before input
    basic_istream&
    _C_ipfx (bool, ios_base::iostate = ios_base::eofbit | ios_base::failbit);

#ifdef _RWSTD_NO_NESTED_CLASS_ACCESS

    // allow access to ios_base::_C_bufmutex() if the resolution
    // of cwg issue 45 is not yet implemented
    struct sentry;
    friend struct sentry;

#endif   // _RWSTD_NO_NESTED_CLASS_ACCESS

    // 27.6.1.1.2
    struct sentry: _RW::__rw_guard {

        // 27.6.1.1.2, p2 - assumes 0 != __strm.rdbuf ()
        _EXPLICIT sentry (basic_istream &__strm, bool __noskipws = false)
            : _RW::__rw_guard (__strm._C_bufmutex ()),
              _C_ok (__strm._C_ipfx (__noskipws).good ()) { /* no-op */ }

        // 27.6.1.1.2, p8
        operator bool () const {
            return _C_ok;
        }

    private:
        bool _C_ok;

        sentry (const sentry&);           // (normally) not defined
        void operator= (const sentry&);   // (normally) not defined
    };

    // 27.6.1.2.3, p1, lwg issue 60
    basic_istream& operator>> (basic_istream& (*__pf)(basic_istream&)) {
        return (*__pf)(*this);
    }

    // 27.6.1.2.3, p2, lwg issue 60
    basic_istream& operator>>(basic_ios<char_type, traits_type>& (*__pf)
                              (basic_ios<char_type, traits_type>&)) {
        return (*__pf)(*this), *this;
    }

    // 27.6.1.2.3, p4, lwg issue 60
    basic_istream& operator>> (ios_base& (*__pf)(ios_base&)) {
        return (*__pf)(*this), *this;
    }


    // 27.6.1.2.2 - Arithmetic Extractors

#ifndef _RWSTD_NO_NATIVE_BOOL

    basic_istream& operator>> (bool &__val) {
        return _RW::__rw_extract (*this, __val);
    }

#endif   // _RWSTD_NO_NATIVE_BOOL

    basic_istream& operator>>(short &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(unsigned short &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(int &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(unsigned int &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(long &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(unsigned long &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(float &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(double &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(long double &__val) {
        return _RW::__rw_extract (*this, __val);
    }

#ifdef _RWSTD_LONG_LONG

    basic_istream& operator>>(_RWSTD_LONG_LONG &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    basic_istream& operator>>(unsigned _RWSTD_LONG_LONG &__val) {
        return _RW::__rw_extract (*this, __val);
    }

#endif   // _RWSTD_LONG_LONG

    basic_istream& operator>>(void* &__val) {
        return _RW::__rw_extract (*this, __val);
    }

    // 27.6.1.2.3, p13
    basic_istream& operator>>(basic_streambuf<char_type, traits_type>*);

    // 27.6.1.3 - Unformatted input functions

    // 27.6.1.3, p3
    int_type get ();

    // 27.6.1.3, p5
    basic_istream& get (char_type&);

    // 27.6.1.3, p7: extract at most n - 1 chars, delim not extracted
    //               always null-terminate, fail if no char extracted
    basic_istream& get (char_type*, streamsize, char_type);

    // 27.6.1.3, p9: extract at most n - 1 chars
    //               always null-terminate, fail if no char extracted
    basic_istream& get (char_type *__s, streamsize __n) {
        return get (__s, __n, this->widen ('\n'));
    }

    // 27.6.1.3, p12: extract up to but not including delim or eof
    basic_istream&
    get (basic_streambuf<char_type, traits_type>& __sb, char_type __delim) {
        return _C_get (__sb, traits_type::to_int_type (__delim));
    }

    // 27.6.1.3, p15
    basic_istream& get (basic_streambuf<char_type, traits_type>& __sb) {
        return get (__sb, this->widen ('\n'));
    }

    // 27.6.1.3, p16: extract at most n - 1, delim extracted but not stored
    //                fail if either 0 or n - 1 chars have been extracted
    basic_istream& getline (char_type*, streamsize, char_type);

    // 27.6.1.3, p23
    basic_istream& getline (char_type *__s, streamsize __n) {
        return getline (__s, __n, this->widen ('\n'));
    }

    // 27.6.1.3, p24: extract at most n including delim
    basic_istream& ignore (streamsize __n = 1,
                           int_type __delim = traits_type::eof ()) {
        // using extension - passing null pointer to ignore input
        return read (0, __n, __delim, _C_eatdelim | _C_eatnull);
    }

    // extension
    basic_istream& read (char_type*, streamsize, int_type, int);

    // 27.6.1.3, p28: extract at most n, fail on eof
    basic_istream& read (char_type*, streamsize);

    // 27.6.1.3, p30: extract at most min (rdbuf()->in_avail(), n))
    streamsize readsome (char_type*, streamsize);

    // 27.6.1.3, p27
    int_type peek ();
      
    // 27.6.1.3, p37
    pos_type tellg ();

    // 27.6.1.3, p38
    basic_istream& seekg (pos_type);

    // 27.6.1.3, p40
    basic_istream& seekg (off_type, ios_base::seekdir);

    // 27.6.1.3, p36
    int sync ();

    // 27.6.1.3, p32
    basic_istream& putback (char_type);

    // 27.6.1.3, p34
    basic_istream& unget ();

    // 27.6.1.3, p2
    streamsize gcount () const {
        return _C_gcount;
    }

    // flags used by read() extension, _C_ipfx(), and _C_unsafe_get()
    enum {
        _C_nullterm = 0x01,   // null-terminate input
        _C_wsterm   = 0x02,   // terminate input on whitespace
        _C_skipws   = 0x04,   // skip leading whitespace
        _C_eatdelim = 0x08,   // extract delimiter
        _C_faileof  = 0x10,   // set ios_base::failbit on eof
        _C_failend  = 0x20,   // set ios_base::failbit on end of buffer
        _C_failnoi  = 0x40,   // set ios_base::failbit on no input
        _C_eatnull  = 0x80    // extract null char
    };

    // does not construct a sentry, does not affect gcount()
    // extracts character unless it is equal to __delim
    int_type _C_unsafe_get (streamsize* = 0,
                            int_type    = traits_type::eof (),
                            int         = 0);

private:

    basic_istream& _C_get (basic_streambuf<char_type, traits_type>&, int_type);

    streamsize _C_gcount;   // number of chars extracted
};



template<class _CharT, class _Traits>
inline _TYPENAME basic_istream<_CharT, _Traits>::int_type
basic_istream<_CharT, _Traits>::
get ()
{
    const sentry __ipfx (*this, true /* noskipws */);

    if (__ipfx)
        return _C_unsafe_get (&_C_gcount, traits_type::eof (),
                              _C_faileof | _C_eatnull);

    return traits_type::eof ();
}


template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
basic_istream<_CharT, _Traits>::
get (char_type &__ch)
{
    const int_type __c = get ();

    if (!traits_type::eq_int_type (__c, traits_type::eof ()))
        traits_type::assign (__ch, traits_type::to_char_type (__c));

    return *this;
}


template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
basic_istream<_CharT, _Traits>::seekg (pos_type __pos)
{
    _RWSTD_ASSERT (0 != this->rdbuf ());

    if (!this->fail ()) {
        _RWSTD_MT_GUARD (this->_C_bufmutex ());

        // 27.6.1.3, p 38 requires that pubseekpos be called with
        // a single argument; the implemented behavior follows
        // the proposed resolution of lwg issue 136
        if (-1 == this->rdbuf ()->pubseekpos (__pos, ios_base::in))
            this->setstate (ios_base::failbit);   // lwg issue 129
    }

    return *this;
}


template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
basic_istream<_CharT, _Traits>::seekg (off_type __off, ios_base::seekdir __dir)
{
    _RWSTD_ASSERT (0 != this->rdbuf ());

    if (!this->fail ()) {
        _RWSTD_MT_GUARD (this->_C_bufmutex ());

        // 27.6.1.3, p 40 requires that pubseekoff be called with
        // two arguments; the implemented behavior follows
        // the proposed resolution of lwg issue 136
        if (-1 == this->rdbuf ()->pubseekoff (__off, __dir, ios_base::in))
            this->setstate (ios_base::failbit);   // lwg issue 129
    }

    return *this;
}


template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
basic_istream<_CharT, _Traits>::
operator>>(basic_streambuf<char_type, traits_type> *__sb)
{
    // lwg issue 60: this is not a formatted input member function
    if (__sb)
        return _C_get (*__sb, traits_type::eof ());

    this->setstate (ios_base::failbit);
    return *this;
}


template<class _CharT, class _Traits>
inline _TYPENAME basic_istream<_CharT, _Traits>::int_type
basic_istream<_CharT, _Traits>::peek ()
{
    _RWSTD_ASSERT (0 != this->rdbuf ());

    const sentry __ipfx (*this, true /* noskipws */);

    int_type __c = traits_type::eof ();

    if (__ipfx) {
        _TRY {
            __c = this->rdbuf ()->sgetc ();
        }
        _CATCH (...) {
            this->setstate (ios_base::badbit | _RW::__rw_rethrow);
        }
    }

    if (traits_type::eq_int_type (__c, traits_type::eof ()))
        this->setstate (ios_base::eofbit);

    return __c;
}


// 27.6.1.2.3, p10
template <class _Traits>
inline basic_istream<char, _Traits>&
operator>> (basic_istream<char, _Traits> &__strm, unsigned char &__c)
{
    return __strm >> _RWSTD_REINTERPRET_CAST (char&, __c);
}

template <class _Traits>
inline basic_istream<char, _Traits>&
operator>> (basic_istream<char, _Traits> &__strm,  signed char &__c)
{
    return __strm >> _RWSTD_REINTERPRET_CAST (char&, __c);
}


// 27.6.1.2.3, p6
template <class _Traits>
inline basic_istream<char, _Traits>&
operator>> (basic_istream<char, _Traits> &__strm, unsigned char *__s)
{
    return __strm >> _RWSTD_REINTERPRET_CAST (char*, __s);
}


template <class _Traits>
inline basic_istream<char, _Traits>&
operator>> (basic_istream<char, _Traits> &__strm, signed char *__s)
{
    return __strm >> _RWSTD_REINTERPRET_CAST (char*, __s);
}


// 27.6.1.2.3, p10
template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
operator>> (basic_istream<_CharT, _Traits> &__strm, _CharT &__c)
{
    // read the first non-space char, set failbit if none read
    __strm.read (&__c, 1, _Traits::eof (),
                 __strm._C_skipws | __strm._C_failnoi | __strm._C_eatnull);
    return __strm;
}


// 27.6.1.2.3, p6
template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
operator>> (basic_istream<_CharT, _Traits> &__strm, _CharT *__s)
{
    _RWSTD_ASSERT (0 != __s);

    // store at most this many chars including terminating null
    const streamsize __maxlen = __strm.width () ?
        __strm.width () : streamsize (_RWSTD_LONG_MAX - 1);

    // read at most __maxlen non-space chars up to the first whitespace
    __strm.read (__s, __maxlen, _Traits::to_int_type (__strm.widen ('\n')),
                   __strm._C_nullterm | __strm._C_wsterm
                 | __strm._C_skipws   | __strm._C_failnoi);

    __strm.width (0);

    return __strm;
}


// 27.6.1.4, p1
template<class _CharT, class _Traits>
inline basic_istream<_CharT, _Traits>&
ws (basic_istream<_CharT, _Traits> &__strm)
{
    // construct a sentry to flush tied stream (if any) and lock
    const _TYPENAME basic_istream<_CharT, _Traits>::sentry
        __ipfx (__strm, true /* noskipws */);

    // if sentry is okay, skip whitespace and set eofbit
    // but not failbit if end-of-file is encountered
    if (__ipfx) {
        _TRY {
            __strm._C_ipfx (false, ios_base::eofbit);
        }
        _CATCH (...) {
            __strm.setstate (ios_base::badbit | _RW::__rw_rethrow);
        }
    }

    return __strm;
}


_EXPORT
template<class _CharT, class _Traits, class _Allocator>
basic_istream<_CharT, _Traits>&
operator>> (basic_istream<_CharT, _Traits>&,
            basic_string<_CharT, _Traits, _Allocator>&);


_EXPORT
template<class _CharT, class _Traits, class _Allocator>
basic_istream<_CharT, _Traits>&
getline (basic_istream<_CharT, _Traits>&,
         basic_string<_CharT, _Traits, _Allocator>&,
         _CharT);


template<class _CharT, class _Traits, class _Allocator>
inline basic_istream<_CharT, _Traits>& 
getline (basic_istream<_CharT, _Traits>&            __is,
         basic_string<_CharT, _Traits, _Allocator>& __str)
{
    return getline (__is, __str, __is.widen ('\n'));
}


// 27.6.1.5
template<class _CharT, class _Traits /* = char_traits<_CharT> */>
class basic_iostream
    : public basic_istream<_CharT, _Traits>,
      public basic_ostream<_CharT, _Traits> 
{
public:

    // prevent ambiguity between types defined in the bases
    typedef _CharT                          char_type;
    typedef _Traits                         traits_type;
    typedef _TYPENAME traits_type::int_type int_type;
    typedef _TYPENAME traits_type::pos_type pos_type;
    typedef _TYPENAME traits_type::off_type off_type;

    _EXPLICIT basic_iostream (basic_streambuf<_CharT, _Traits> *__sb)
        : basic_istream<_CharT, _Traits>(__sb), 
          basic_ostream<_CharT, _Traits>(__sb)
        { /* 27.6.1.5.1, p1 */ }
};

}   // namespace std


#if _RWSTD_DEFINE_TEMPLATE (_BASIC_ISTREAM)
   // implementation file included here instead of at the end
   // of the header to work around a IBM VAC++ 7.0 bug #448
#  include <istream.cc>
#endif   // _RWSTD_DEFINE_TEMPLATE (_BASIC_ISTREAM)


_RWSTD_NAMESPACE (std) {

#if _RWSTD_INSTANTIATE (_BASIC_ISTREAM, _CHAR)

_RWSTD_INSTANTIATE_2 (class _RWSTD_EXPORT
                      basic_istream<char, char_traits<char> >);

_RWSTD_INSTANTIATE_FUN_1 (_RWSTD_EXPORT istream&
                          operator>> (istream&, string&));

_RWSTD_INSTANTIATE_FUN_1 (_RWSTD_EXPORT istream&
                          getline (istream&, string&, char));

#endif   // _RWSTD_INSTANTIATE (_BASIC_ISTREAM, _CHAR)

#if _RWSTD_INSTANTIATE (_BASIC_ISTREAM, _WCHAR_T)

_RWSTD_INSTANTIATE_2 (class _RWSTD_EXPORT
                      basic_istream<wchar_t, char_traits<wchar_t> >);

_RWSTD_INSTANTIATE_FUN_1 (_RWSTD_EXPORT wistream&
                          operator>> (wistream&, wstring&));

_RWSTD_INSTANTIATE_FUN_1 (_RWSTD_EXPORT wistream&
                          getline (wistream&, wstring&, wchar_t));
#endif   // _RWSTD_INSTANTIATE (_BASIC_ISTREAM, _WCHAR_T)

}   // namespace std


#endif   // _RWSTD_ISTREAM_INCLUDED
